/*
* Copyright 2009 NOR_/D Labs <http://labs.nortd.com>
*
* This file is part of SceneExpression.
* SceneExpression is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License 
* as published by the Free Software Foundation, either version 3 of 
* the License, or (at your option) any later version. For details
* see <http://www.gnu.org/licenses/>.
*
* * *
* 
*/

#include <sx/Node.h>

using namespace sx;


Node::Node() {
    _xform = new osg::PositionAttitudeTransform();
    _xform->setUserData(this);
    _geode = new osg::Geode();
    _xform->addChild(_geode.get());
}


osg::Matrix Node::getTransform() {
    osg::Matrix mat;
    _xform->computeLocalToWorldMatrix(mat, NULL);
    return mat;
}
osg::Matrix Node::getWorldTransform() {
    return _geode->getWorldMatrices()[0];
}



// Resetting Transforms
//
void Node::resetTransform() {
    resetPosition();
    resetRotation();
    resetScale();
    resetPivot();
}
void Node::resetPosition() {
    _xform->setPosition(osg::Vec3(0,0,0));
}
void Node::resetRotation() {
    _xform->setAttitude(osg::Quat());
}
void Node::resetScale() {
    _xform->setScale(osg::Vec3(1,1,1));
}
void Node::resetPivot() {
    _xform->setPivotPoint(osg::Vec3(0,0,0));
}



// common states
//
void Node::setVisible( bool bShow ) {
    if(bShow) {
        _xform->removeCullCallback(_xform->getCullCallback());
    } else {
        _xform->setCullCallback(new ForceCullCallback());
    }
}
void Node::enableLighting() {
    osg::StateSet* stateset = _xform->getOrCreateStateSet();
    stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
}
void Node::disableLighting() {
    osg::StateSet* stateset = _xform->getOrCreateStateSet();
    stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
}
void Node::enableDepthTest() {
    osg::StateSet* stateset = _xform->getOrCreateStateSet();
    stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
}
void Node::disableDepthTest() {
    osg::StateSet* stateset = _xform->getOrCreateStateSet();
    stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
}
void Node::enableBlending() {
    osg::StateSet* stateset = _xform->getOrCreateStateSet();
    osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc();
    blendFunc->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
    stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
    stateset->setAttributeAndModes(blendFunc.get(), osg::StateAttribute::ON); 
}
void Node::disableBlending() {
    osg::StateSet* stateset = _xform->getOrCreateStateSet();
    stateset->setMode(GL_BLEND, osg::StateAttribute::OFF);
}

void Node::enableAutoNormalization() {
    osg::StateSet* stateset = _xform->getOrCreateStateSet();
    stateset->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
}
void Node::disableAutoNormalization() {
    osg::StateSet* stateset = _xform->getOrCreateStateSet();
    stateset->setMode(GL_NORMALIZE, osg::StateAttribute::OFF);
}


// Scene Graph Hierarchy
//

bool Node::addChild( Node* node ) {
    return _xform->addChild(node->getOsgXForm().get());
}
bool Node::insertChild( unsigned int index, Node* node ) {
    return _xform->insertChild(index, node->getOsgXForm().get());
}
bool Node::removeChild( Node* node ) {
    return _xform->removeChild(node->getOsgXForm().get());
}
bool Node::replaceChild( Node* origNode, Node* newNode ) {
    return _xform->replaceChild(origNode->getOsgXForm().get(), newNode->getOsgXForm().get());
}
unsigned int Node::getNumChildren() {
    return _xform->getNumChildren();
}
bool Node::setChild( unsigned int index, Node* node ) {
    return _xform->setChild(index, node->getOsgXForm().get());
}
Node* Node::getChild( unsigned int index ) {
    osg::Node* node = _xform->getChild(index);
    Node* sxnode = dynamic_cast<Node*>(node->getUserData());
    if( !sxnode ) {
        osg::notify(osg::WARN) << "in getChild, not a Node at index" << std::endl;
    }
    return sxnode;
}
bool Node::containsNode( Node* node ) {
    return _xform->containsNode(node->getOsgXForm().get());
}
unsigned int Node::getChildIndex( Node* node ) {
    return _xform->getChildIndex(node->getOsgXForm().get());
}



// Scene Graph Geometry
//

bool Node::addDrawable( Drawable* drawable ) {
    return _geode->addDrawable(drawable->getOsgDrawable().get());
}
bool Node::removeDrawable( Drawable* drawable ) {
    return _geode->removeDrawable(drawable->getOsgDrawable().get());
}
bool Node::replaceDrawable( Drawable* origDrawable, Drawable* newDrawable ) {
    return _geode->replaceDrawable(origDrawable->getOsgDrawable().get(), newDrawable->getOsgDrawable().get());    
}
unsigned int Node::getNumDrawables() {
    return _geode->getNumDrawables();
}
bool Node::setDrawable( unsigned int index, Drawable* drawable ) {
    return _geode->setDrawable( index, drawable->getOsgDrawable().get());
}
Drawable* Node::getDrawable( unsigned int index ) {
    osg::Drawable* drawable = _geode->getDrawable(index);
    Drawable* sxdrawable = dynamic_cast<Drawable*>(drawable->getUserData());
    if( !sxdrawable ) {
        osg::notify(osg::WARN) << "in getDrawable, not a Drawable at index" << std::endl;
    }
    return sxdrawable;
}
bool Node::containsDrawable( Drawable* drawable ) {
    return _geode->containsDrawable(drawable->getOsgDrawable().get());
}
unsigned int Node::getDrawableIndex( Drawable* drawable ) {
    return _geode->getDrawableIndex(drawable->getOsgDrawable().get());
}



    
// Translation
//
float Node::getX() {
    return _xform->getPosition()[0];
}
float Node::getY() {
    return _xform->getPosition()[1];
}
float Node::getZ() {
    return _xform->getPosition()[2];
}
osg::Vec3 Node::getPosition() {
    return _xform->getPosition();
}

void Node::setX( float x ) {
    const osg::Vec3d& pos = _xform->getPosition(); 
    setPosition(x, pos[1], pos[2]);    
}
void Node::setY( float y ) {
    const osg::Vec3d& pos = _xform->getPosition(); 
    setPosition(pos[0], y, pos[2]);    
}
void Node::setZ( float z ) {
    const osg::Vec3d& pos = _xform->getPosition(); 
    setPosition(pos[0], pos[1], z);    
}
void Node::setPosition( float x, float y, float z ) {
    _xform->setPosition(osg::Vec3(x,y,z));
}
void Node::setPosition( const osg::Vec3& pnt ) {
    _xform->setPosition(pnt);
}

void Node::addX( float x ) {
    const osg::Vec3d& pos = _xform->getPosition(); 
    setPosition(pos[0]+x, pos[1], pos[2]);    
}
void Node::addY( float y ) {
    const osg::Vec3d& pos = _xform->getPosition(); 
    setPosition(pos[0], pos[1]+y, pos[2]);    
}
void Node::addZ( float z ) {
    const osg::Vec3d& pos = _xform->getPosition(); 
    setPosition(pos[0], pos[1], pos[2]+z);    
}
void Node::addPosition( float x, float y, float z ) {
    const osg::Vec3d& pos = _xform->getPosition();
    setPosition(pos[0]+x,pos[1]+y,pos[2]+z);
}
void Node::addPosition( const osg::Vec3& pnt ) {
    const osg::Vec3d& pos = _xform->getPosition();
    _xform->setPosition(pnt+pos);
}



// Rotation
//

float Node::getRotationAngle() {
    double angle;
    osg::Vec3f axis;
    _xform->getAttitude().getRotate(angle, axis);
    return static_cast<float>(angle);
}
osg::Vec3 Node::getRotationAxis() {
    double angle;
    osg::Vec3f axis;
    _xform->getAttitude().getRotate(angle, axis);
    return axis;
}
osg::Quat Node::getRotation() {
    return _xform->getAttitude();
}

void Node::setRotation( float angle, float x, float y, float z ) {
    _xform->setAttitude(osg::Quat(angle, osg::Vec3f(x,y,z)));
}
void Node::setRotation( const osg::Quat& quat ) {
    _xform->setAttitude(quat);
}
void Node::setRotation( float x1, float y1, float z1, float x2, float y2, float z2 ) {
    osg::Quat quat;
    quat.makeRotate(osg::Vec3f(x1,y1,z1), osg::Vec3f(x2,y2,z2));
    _xform->setAttitude(quat);
}
void Node::setRotation( const osg::Vec3f& vec1, const osg::Vec3f& vec2 ) {
    osg::Quat quat;
    quat.makeRotate(vec1, vec2);
    _xform->setAttitude(quat);
}
void Node::setRotation( float slerpPct, const osg::Quat& from, const osg::Quat& to ) {
    osg::Quat quat;
    quat.slerp(slerpPct, from, to);
    _xform->setAttitude(quat);
}

void Node::addRotationX( float x ) {
    const osg::Quat& attitude = _xform->getAttitude();
    _xform->setAttitude(attitude * osg::Quat(x, osg::Vec3(1,0,0)));
}
void Node::addRotationY( float y ) {
    const osg::Quat& attitude = _xform->getAttitude();
    _xform->setAttitude(attitude * osg::Quat(y, osg::Vec3(0,1,0)));
}
void Node::addRotationZ( float z ) {
    const osg::Quat& attitude = _xform->getAttitude();
    _xform->setAttitude(attitude * osg::Quat(z, osg::Vec3(0,0,1)));
}
void Node::addRotation( float angle, float x, float y, float z ) {
    const osg::Quat& attitude = _xform->getAttitude();
    _xform->setAttitude(attitude * osg::Quat(angle, osg::Vec3f(x,y,z)));
}
void Node::addRotation( const osg::Quat& quat ) {
    const osg::Quat& attitude = _xform->getAttitude();
    _xform->setAttitude(attitude * quat);
}
void Node::addRotation( float x1, float y1, float z1, float x2, float y2, float z2 ) {
    const osg::Quat& attitude = _xform->getAttitude();
    osg::Quat quat;
    quat.makeRotate(osg::Vec3f(x1,y1,z1), osg::Vec3f(x2,y2,z2));
    _xform->setAttitude(attitude * quat);
}
void Node::addRotation( const osg::Vec3f& vec1, const osg::Vec3f& vec2 ) {
    const osg::Quat& attitude = _xform->getAttitude();
    osg::Quat quat;
    quat.makeRotate(vec1, vec2);
    _xform->setAttitude(attitude * quat);
}


// Scale
//    
float Node::getScaleX() {
    return _xform->getScale()[0];
}
float Node::getScaleY() {
    return _xform->getScale()[1];
}
float Node::getScaleZ() {
    return _xform->getScale()[2];
}
osg::Vec3 Node::getScale() {
    return _xform->getScale();
}

void Node::setScaleX( float x ) {
    const osg::Vec3d& scale = _xform->getScale(); 
    setScale(x, scale[1], scale[2]);    
}
void Node::setScaleY( float y ) {
    const osg::Vec3d& scale = _xform->getScale(); 
    setScale(scale[0], y, scale[2]);    
}
void Node::setScaleZ( float z ) {
    const osg::Vec3d& scale = _xform->getScale(); 
    setScale(scale[0], scale[1], z);    
}
void Node::setScale( float x, float y, float z ) {
    _xform->setScale(osg::Vec3(x,y,z));
}
void Node::setScale( const osg::Vec3& scale ) {
    _xform->setScale(scale);
}
void Node::setScale( float scale ) {
    _xform->setScale(osg::Vec3(scale,scale,scale));
}

void Node::multScaleX( float x ) {
    const osg::Vec3d& scale = _xform->getScale(); 
    setScale(scale[0]*x, scale[1], scale[2]);    
}
void Node::multScaleY( float y ) {
    const osg::Vec3d& scale = _xform->getScale(); 
    setScale(scale[0], scale[1]*y, scale[2]);    
}
void Node::multScaleZ( float z ) {
    const osg::Vec3d& scale = _xform->getScale(); 
    setScale(scale[0], scale[1], scale[2]*z);    
}
void Node::multScale( float x, float y, float z ) {
    const osg::Vec3d& scale = _xform->getScale();
    setScale(scale[0]*x,scale[1]*y,scale[2]*z);
}
void Node::multScale( const osg::Vec3& scale ) {
    const osg::Vec3d& current = _xform->getScale();
    _xform->setScale(osg::Vec3d(scale[0]*current[0], scale[1]*current[1], scale[2]*current[2]));
}



// Pivot
//    
float Node::getPivotX() {
    return _xform->getPivotPoint()[0];
}
float Node::getPivotY() {
    return _xform->getPivotPoint()[1];
}
float Node::getPivotZ() {
    return _xform->getPivotPoint()[2];
}
osg::Vec3 Node::getPivot() {
    return _xform->getPivotPoint();
}

void Node::setPivotX( float x ) {
    const osg::Vec3d& pivot = _xform->getPivotPoint();
    _xform->setPivotPoint(osg::Vec3d(x, pivot[1], pivot[2]));
}
void Node::setPivotY( float y ) {
    const osg::Vec3d& pivot = _xform->getPivotPoint();
    _xform->setPivotPoint(osg::Vec3d(pivot[0], y, pivot[2]));
}
void Node::setPivotZ( float z ) {
    const osg::Vec3d& pivot = _xform->getPivotPoint();
    _xform->setPivotPoint(osg::Vec3d(pivot[0], pivot[1], z));
}
void Node::setPivot( float x, float y, float z ) {
    _xform->setPivotPoint(osg::Vec3d(x, y, z));
}
void Node::setPivot( const osg::Vec3& pnt ) {
    _xform->setPivotPoint(pnt);
}

void Node::addPivotX( float x ) {
    const osg::Vec3d& pivot = _xform->getPivotPoint();
    _xform->setPivotPoint(osg::Vec3d(pivot[0]+x, pivot[1], pivot[2]));  
}
void Node::addPivotY( float y ) {
    const osg::Vec3d& pivot = _xform->getPivotPoint();
    _xform->setPivotPoint(osg::Vec3d(pivot[0], pivot[1]+y, pivot[2]));  
}
void Node::addPivotZ( float z ) {
    const osg::Vec3d& pivot = _xform->getPivotPoint();
    _xform->setPivotPoint(osg::Vec3d(pivot[0], pivot[1], pivot[2]+z));  
}
void Node::addPivot( float x, float y, float z ) {
    const osg::Vec3d& pivot = _xform->getPivotPoint();
    _xform->setPivotPoint(osg::Vec3d(pivot[0]+x, pivot[1]+y, pivot[2]+z));  
}
void Node::addPivot( const osg::Vec3& pnt ) {
    const osg::Vec3d& pivot = _xform->getPivotPoint();
    _xform->setPivotPoint(pnt+pivot);  
}


